package com.zmn.oms.third.daowei.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.ltsopensource.core.domain.Job;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.zmn.base.common.data.common.dro.area.AreaDRO;
import com.zmn.base.common.data.dubbo.interfaces.area.AreaListRemoteService;
import com.zmn.base.product.common.dto.product.ProductBaseQuery;
import com.zmn.base.product.common.dto.product.ec.ProductBaseDRO;
import com.zmn.base.product.dubbo.interfaces.product.foreign.ProductForeignListRemoteService;
import com.zmn.common.constant.GlobalConsts;
import com.zmn.common.dictionary.GlobalDict;
import com.zmn.common.dto2.ResponseDTO;
import com.zmn.common.utils.collection.CollectionUtil;
import com.zmn.common.utils.date.DateUtil;
import com.zmn.common.utils.math.MoneyUtil;
import com.zmn.common.utils.string.StringUtil;
import com.zmn.manager.lts.interfaces.task.TaskManager;
import com.zmn.oms.business.interfaces.log.OrderLogBService;
import com.zmn.oms.business.interfaces.notifylog.OrderNotifyLogBService;
import com.zmn.oms.business.interfaces.remark.OrderRemarkBService;
import com.zmn.oms.common.constant.OrderConsts;
import com.zmn.oms.common.constant.OrderLogConsts;
import com.zmn.oms.common.constant.OrderStatusConsts;
import com.zmn.oms.common.exception.OmsBaseException;
import com.zmn.oms.manager.utils.TaskConsts;
import com.zmn.oms.model.bo.order.OrderBO;
import com.zmn.oms.model.dto.order.proxy.*;
import com.zmn.oms.model.dto.product.OrderProductDTO;
import com.zmn.oms.model.entity.log.OrderLog;
import com.zmn.oms.model.entity.order.OrderExtend;
import com.zmn.oms.model.entity.work.OrderWork;
import com.zmn.oms.services.interfaces.order.OrderChangeService;
import com.zmn.oms.services.interfaces.order.OrderExtendService;
import com.zmn.oms.services.interfaces.work.OrderWorkService;
import com.zmn.oms.third.base.util.ThirdOrderUtil;
import com.zmn.oms.third.core.tracker.FacadeTracker;
import com.zmn.oms.third.daowei.DaoweiApiInvoker;
import com.zmn.oms.third.daowei.DaoweiConsts;
import com.zmn.oms.third.daowei.dto.*;
import com.zmn.oms.third.daowei.service.DaoweiService;
import com.zmn.oms.third.todm.Todm;
import com.zmn.oms.zmn.aop.OrderWorkLogUtils;
import com.zmn.oms.zmn.business.interfaces.proxy.ProxyOrderBService;
import com.zmn.oms.zmn.business.interfaces.work.autoflow.WorkFlowContextBService;
import com.zmn.pay.common.constant.PayConsts;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 类描述：
 *
 * @author lhl
 * @date 2020/02/21 15:13
 */
@Slf4j
@Service("daoweiService")
public class DaoweiServiceImpl implements DaoweiService {

    @Autowired
    private TaskManager taskManager;

    @Reference(version = com.zmn.base.plat.engine.common.constant.DubboConsts.INTERFACE_VERSION , check = false)
    private ProductForeignListRemoteService productForeignListRemoteService;
    @Reference(version = com.zmn.base.plat.engine.common.constant.DubboConsts.INTERFACE_VERSION , check = false)
    private AreaListRemoteService areaListRemoteService;

    @Autowired
    private ProxyOrderBService proxyOrderBService;
    @Autowired
    private WorkFlowContextBService workFlowContextBService;
    @Autowired
    private OrderWorkService orderWorkService;
    @Autowired
    private OrderLogBService orderLogBService;
    @Autowired
    private OrderRemarkBService orderRemarkBService;
    @Resource
    private OrderNotifyLogBService orderNotifyLogBService;
    @Autowired
    private OrderExtendService orderExtendService;
    @Autowired
    private OrderChangeService orderChangeService;

    /**
     * 新建订单
     * @param req
     * @return
     */
    @Override
    public CreateOrderRsp createOrder(CreateOrderReq req) {
        CreateOrderRsp rsp = new CreateOrderRsp();
        FacadeTracker.FacadeTrackerCore tracker = FacadeTracker.getTracker();

        try {
            // 是否是到位直选订单
            boolean isQuick = Objects.equals(1, req.getIsQuick());

            DaoweiConsts.ChannelInfo channelInfo = DaoweiConsts.channelInfo(req.getAppkey(), isQuick);
            if (channelInfo == null) {
                log.info("【到位】创建订单失败，未能识别Appkey。 CreateOrderReq=[{}]", JSON.toJSONString(req));

                rsp.setMsg("未能识别Appkey");
                rsp.setStatus("error");
                return rsp;
            }

            req.setChannelId(channelInfo.getId());

            // 提取新单数据，构建录单对象
            CreateOrderDTO createOrderDTO = buildCreateOrderDTO(req, channelInfo);
            if (createOrderDTO != null) {
                boolean unknowAddress = createOrderDTO.getCityId() == null;
                boolean unknowProduct = Objects.equals(OrderConsts.CHANNEL_DISABLED_PRODUCT_ID,
                        createOrderDTO.getOrderProductList().get(0).getProductId());
                // 地址解析失败/产品解析失败时，默认地址写重庆，产品设置为其他，避免自动派单
                if (unknowAddress || unknowProduct) {
                    ThirdOrderUtil.fillDefaultOrderInfo(createOrderDTO, "；产品未能自动识别，需人工确认");
                }
            }

            // 保存订单
            OrderBO orderBO = proxyOrderBService.createOrder(createOrderDTO);
            Long orderId = orderBO.getOrderWork().getOrderId();

            log.info("【{}】保存订单成功 OrderId:[{}]", channelInfo.getName(), orderId);
            tracker.orderId(orderId);

            rsp.setOrderId(String.valueOf(orderId));

            // 设置超时支付检查：1小时内没有支付，自动弃单
            addPrepayTimeoutCancelJob(orderId);
            tracker.remark("设置超时支付检查：1小时内没有支付，自动弃单；");

            if (isQuick) {
                // 直选订单，设置30秒后发起抢单
                addRushToAcceptJob(orderId, createOrderDTO.getOuterId());
                tracker.remark("直选订单，设置30秒后发起抢单；");
            }

        } catch (Exception e) {
            log.error("【{}】创建订单失败：{}", DaoweiConsts.getChannelName(req.getChannelId()), e.getMessage(), e);
            tracker.error(e, "创建订单失败");

            rsp.setMsg("创建订单失败");
            rsp.setStatus("error");
        }

        return rsp;
    }

    /**
     * 订单预支付
     * @param orderPay
     * @return
     */
    @Override
    public BaseRsp prepayOrder(OrderPay orderPay) {
        BaseRsp rsp = new BaseRsp();
        FacadeTracker.FacadeTrackerCore tracker = FacadeTracker.getTracker();

        try {
            Long orderId = Long.parseLong(orderPay.getOrderId());
            OrderWork dbOrderWork = orderWorkService.findOrderWorkByKeySrcMaster(orderId, orderId);

            if (dbOrderWork == null) {
                log.info("【到位】订单不存在: orderId=[{}]", orderPay.getOrderId());

                rsp.setStatus("error");
                rsp.setMsg("订单不存在");
                return rsp;
            }
            tracker.orderId(orderId);

            Integer channelId = dbOrderWork.getChannelId();
            orderPay.setChannelId(channelId);

            PayOrderDTO payOrderDTO = new PayOrderDTO();
            payOrderDTO.setOrderId(orderId);
            payOrderDTO.setOuterId(dbOrderWork.getOuterId());
            payOrderDTO.setChannelId(channelId);
            // 渠道预付
            payOrderDTO.setOrderPayType(OrderConsts.API_ORDER_PAY_TYPE_CHANNEL_PREPAID);

            // 支付总金额 = 实际支付金额 + 到位代金券金额
            BigDecimal total = new BigDecimal(Optional.ofNullable(orderPay.getBill()).orElse(0d))
                    .add(new BigDecimal(Optional.ofNullable(orderPay.getDaowayCouponBill()).orElse(0d)));
            Integer amount = MoneyUtil.parseToFen(total.doubleValue());

            payOrderDTO.setPayAmount(amount);
            payOrderDTO.setPayTime(new Date());

            //添加log
            StringBuilder sb = new StringBuilder();
            if (orderPay.getBill() != null) {
                sb.append("；支付金额:" + orderPay.getBill());
            }
            if (orderPay.getDaowayCouponBill() != null) {
                sb.append("；到位代金券金额:" + orderPay.getDaowayCouponBill());
            }
            if (orderPay.getShopCouponBill() != null) {
                sb.append("；商家代金券金额:" + orderPay.getShopCouponBill());
            }
            payOrderDTO.setOperatorRemark(sb.toString());
            tracker.remark(payOrderDTO.getOperatorRemark());

            // 优惠活动
            long beginTime = 1606752000000L;    // 2020-12-01 00:00:00
            long endTime = 1672502399000L;      // 2022-12-31 23:59:59
            Long currentTime = System.currentTimeMillis();  // 当前时间
            Double shopCouponBill = orderPay.getShopCouponBill();
            if (currentTime >= beginTime && currentTime <= endTime &&
                    Objects.nonNull(shopCouponBill) && shopCouponBill > 0.0) {

                // 设置优惠金额
                payOrderDTO.setDiscountAmount(MoneyUtil.parseToFen(shopCouponBill));
            }

            payOrderDTO.setOperatorType(GlobalConsts.OPERATE_USER_TYPE_CHANNEL);
            payOrderDTO.setOperatorId((long) GlobalConsts.OPERATE_USER_TYPE_SYSTEM);
            payOrderDTO.setOperator("系统");

            // 支付
            log.info("【{}】预支付: PayOrderDTO=[{}]", DaoweiConsts.getChannelName(channelId),
                    JSON.toJSONString(payOrderDTO));
            proxyOrderBService.payOrder(payOrderDTO);

            // 支付成功，应设置订单自动流转
            workFlowContextBService.asyncAutoFlow(dbOrderWork.getOrderId(),dbOrderWork.getWorkId());
            tracker.remark("支付成功，设置订单自动流转；");
        } catch (Exception e) {
            log.error("【{}】预支付失败: {}", DaoweiConsts.getChannelName(orderPay.getChannelId()), e.getMessage(), e);
            tracker.error(e, "预支付失败");

            rsp.setMsg(e.getMessage());
            rsp.setStatus("error");
        }

        return rsp;
    }

    /**
     * 二次支付（补差价）
     * @param secondOrderPay
     * @return
     */
    @Override
    public BaseRsp secondPayOrder(SecondOrderPay secondOrderPay) {
        BaseRsp rsp = new BaseRsp();
        FacadeTracker.FacadeTrackerCore tracker = FacadeTracker.getTracker();

        try {
            Long orderId = Long.parseLong(secondOrderPay.getOrderId());
            OrderWork dbOrderWork = orderWorkService.findOrderWorkByKeySrcMaster(orderId, orderId);

            if (dbOrderWork == null) {
                log.info("【到位】订单不存在: orderId=[{}]", secondOrderPay.getOrderId());

                rsp.setStatus("error");
                rsp.setMsg("订单不存在");
                return rsp;
            }
            tracker.orderId(dbOrderWork.getOrderId());

            // 是否抵扣师傅收款额
            boolean subMasterAmount = true;
            if (Optional.ofNullable(dbOrderWork.getMasterAmount()).orElse(0) <= 0) {
                // 不存在师傅收款额时（比如师傅上门报价前，用户在渠道方支付尾款），不抵扣师傅收款额，只记录渠道代收金额
                subMasterAmount = false;
            }

            Integer channelId = dbOrderWork.getChannelId();
            secondOrderPay.setChannelId(channelId);

            PayOrderDTO payOrderDTO = new PayOrderDTO();
            payOrderDTO.setOrderId(orderId);
            payOrderDTO.setOuterId(dbOrderWork.getOuterId());
            payOrderDTO.setChannelId(channelId);
            // 渠道二次支付/补差价（师傅支付）
            payOrderDTO.setOrderPayType(OrderConsts.API_ORDER_PAY_TYPE_CHANNEL_MASTER_AMOUNT);
            payOrderDTO.setPayAmount(MoneyUtil.parseToFen(secondOrderPay.getBill()));
            payOrderDTO.setPayTime(new Date());
            payOrderDTO.setSubMasterAmount(subMasterAmount);
            // 如果不抵扣师傅收款额（没有师傅收款额，即师傅尚未选服务项），不完成订单
            payOrderDTO.setCompleteOrder(subMasterAmount);

            //添加log
            StringBuilder sb = new StringBuilder();
            sb.append("；原预付金额:").append(MoneyUtil.parseToYuanStr(dbOrderWork.getChannelPrepayAmount())).append("元")
                .append("；补差价金额:").append(MoneyUtil.parseToYuanStr(payOrderDTO.getPayAmount())).append("元");
            if (!subMasterAmount) {
                sb.append("；师傅未报价，本次支付不抵扣师傅收款额");
            }
            payOrderDTO.setOperatorRemark(sb.toString());

            payOrderDTO.setOperatorType(GlobalConsts.OPERATE_USER_TYPE_CHANNEL);
            payOrderDTO.setOperatorId((long) GlobalConsts.OPERATE_USER_TYPE_CHANNEL);
            payOrderDTO.setOperator(GlobalConsts.getOperatorTypeName(GlobalConsts.OPERATE_USER_TYPE_CHANNEL));

            // 支付
            log.info("【{}】补差价支付: PayOrderDTO=[{}]", DaoweiConsts.getChannelName(channelId),
                    JSON.toJSONString(payOrderDTO));
            proxyOrderBService.payOrder(payOrderDTO);
            tracker.remark(payOrderDTO.getOperatorRemark());
        } catch (Exception e) {
            log.error("【{}】补差价支付失败: {}",
                    DaoweiConsts.getChannelName(secondOrderPay.getChannelId()), e.getMessage(), e);
            tracker.error(e, "补差价支付失败");

            rsp.setStatus("error");
            rsp.setMsg("订单支付异常");
        }

        return rsp;
    }

    /**
     * 取消订单，一般用于用户在未支付时的取消订单操作
     * @param cancelOrderReq
     * @return
     */
    @Override
    public CancelOrderRsp cancelOrder(CancelOrderReq cancelOrderReq) {
        CancelOrderRsp rsp = new CancelOrderRsp();
        FacadeTracker.FacadeTrackerCore tracker = FacadeTracker.getTracker();
        try {
            boolean isFromChannel = Optional.ofNullable(cancelOrderReq.getFromChannel()).orElse(false);

            Long orderId = Long.parseLong(cancelOrderReq.getOrderId());
            OrderWork dbOrderWork = orderWorkService.findOrderWorkByKey(orderId, orderId);

            if (dbOrderWork == null) {
                log.info("【到位】订单不存在: orderId=[{}]", cancelOrderReq.getOrderId());

                rsp.setStatus("error");
                rsp.setMsg("订单不存在");
                return rsp;
            }
            tracker.orderId(dbOrderWork.getOrderId());

            Integer channelId = dbOrderWork.getChannelId();
            int intChannelId = channelId.intValue();
            if (DaoweiConsts.CHANNEL_ID_ZX != intChannelId
                && DaoweiConsts.CHANNEL_ID_APP != intChannelId
                && DaoweiConsts.CHANNEL_ID_DTW != intChannelId
                && DaoweiConsts.CHANNEL_ID_KYX != intChannelId) {
                log.info("【到位】非到位订单，不可操作: orderId=[{}]", cancelOrderReq.getOrderId());
                rsp.setStatus("error");
                rsp.setMsg("非到位订单，不可操作");
                return rsp;
            }

            cancelOrderReq.setChannelId(channelId);

            String logRemark = isFromChannel ? "；渠道发起取消" : "；啄木鸟发起取消";
            if (StringUtils.isNotBlank(cancelOrderReq.getNote())) {
                logRemark = logRemark + "；取消理由：" + cancelOrderReq.getNote();
            }

            CancelOrderDTO cancelOrderDTO = new CancelOrderDTO();
            cancelOrderDTO.setOrderId(orderId);
            cancelOrderDTO.setOuterId(dbOrderWork.getOuterId());
            cancelOrderDTO.setChannelId(dbOrderWork.getChannelId());
            cancelOrderDTO.setRemark(logRemark);
            cancelOrderDTO.setOperatorType(
                    isFromChannel ? GlobalConsts.OPERATE_USER_TYPE_CHANNEL : GlobalConsts.OPERATE_USER_TYPE_SYSTEM);

            log.info("【{}】取消订单: CancelOrderDTO:[{}]", DaoweiConsts.getChannelName(channelId),
                    JSON.toJSONString(cancelOrderDTO));
            proxyOrderBService.cancelOrder(cancelOrderDTO);
            tracker.remark(cancelOrderDTO.getRemark());
        } catch (Exception e) {
            log.error("【{}】取消订单失败: {}",
                    DaoweiConsts.getChannelName(cancelOrderReq.getChannelId()), e.getMessage(), e);
            tracker.error(e, "取消订单失败");

            rsp.setStatus("error");
            rsp.setMsg("取消失败");
            if (e instanceof OmsBaseException) {
                rsp.setMsg(e.getMessage());
            }
        }

        return rsp;
    }

    /**
     * 申请退款，一般用于用户在已支付后的取消订单操作
     * @param cancelOrderReq
     * @return
     */
    @Override
    public CancelOrderRsp applyRefund(CancelOrderReq cancelOrderReq) {
        CancelOrderRsp rsp = new CancelOrderRsp();
        FacadeTracker.FacadeTrackerCore tracker = FacadeTracker.getTracker();
        try {
            Long orderId = Long.parseLong(cancelOrderReq.getOrderId());
            OrderWork dbOrderWork = orderWorkService.findOrderWorkByKey(orderId, orderId);

            if (dbOrderWork == null) {
                log.info("【到位】订单不存在: orderId=[{}]", cancelOrderReq.getOrderId());

                rsp.setStatus("error");
                rsp.setMsg("订单不存在");
                return rsp;
            }
            tracker.orderId(dbOrderWork.getOrderId());

            Integer channelId = dbOrderWork.getChannelId();
            cancelOrderReq.setChannelId(channelId);

            // 记录日志
            try {
                StringBuilder logStr = new StringBuilder("用户申请退款");

                String bill = String.format("%.2f", Optional.ofNullable(cancelOrderReq.getBill()).orElse(0d));
                logStr.append("，申请退款金额：").append(bill).append("元");

                String note = Optional.ofNullable(cancelOrderReq.getNote()).orElse("");
                logStr.append("，申请退款理由：").append(note);

                String breakAppointment = cancelOrderReq.getBreakAppointment();
                if (StringUtil.isNotBlank(breakAppointment)) {
                    if ("0".equals(breakAppointment.trim())) {
                        logStr.append("，未申请爽约赔付");
                    }
                    else if ("1".equals(breakAppointment.trim())) {
                        logStr.append("，申请爽约赔付");
                    }
                }
                logStr.append("；");

                OrderLogDTO orderLogDTO = new OrderLogDTO();
                orderLogDTO.setOrderId(orderId);
                orderLogDTO.setWorkId(orderId);
                orderLogDTO.setOperatorLogRemark(logStr.toString());

                saveOrderLog(orderId, orderLogDTO);
                tracker.remark(orderLogDTO.getOperatorLogRemark());
            } catch (Exception e) {
                log.error("【{}】保存申请退款操作日志失败: orderId=[{}], {}",
                        DaoweiConsts.getChannelName(channelId), orderId, e.getMessage());
                tracker.error(e, "保存申请退款操作日志失败");
            }

            /*
            // 回复审核结果
            OrderWorkLogChange orderWorkLogChange = new OrderWorkLogChange();
            orderWorkLogChange.setLogType(OrderLogConsts.ORDER_LOG_TYPE_CHANNEL_REMARK);
            orderWorkLogChange.setOpTime(new Date());
            orderWorkLogChange.setExtData("applyRefund");
            orderWorkLogChange.setMqMsgKey("none");
            orderWorkLogChange.setOrderId(orderId);
            orderWorkLogChange.setChannelId(dbOrderWork.getChannelId());
            orderWorkLogChange.setStatus(dbOrderWork.getStatus());
            orderWorkLogChange.setResultStatus(dbOrderWork.getResultStatus());

            OrderExtend orderExtend = orderExtendService.findMiniOrderExtendByOrderId(orderId);
            orderWorkLogChange.setExtOuterData(orderExtend.getExtOuterData());
            Todm.tracker().addRemark("生成“回复审核结果”通知事件；");
            orderChangeService.changeStatusSyncToApi(orderWorkLogChange);
            */
        } catch (Exception e) {
            log.error("【{}】申请退款失败: {}",
                    DaoweiConsts.getChannelName(cancelOrderReq.getChannelId()), e.getMessage(), e);
            tracker.error(e, "申请退款失败");

            rsp.setStatus("error");
            rsp.setMsg("退款失败");
        }

        return rsp;
    }

    /**
     * 评价
     * @param commentReq
     * @return
     */
    @Override
    public BaseRsp comment(CommentReq commentReq) {
        BaseRsp rsp = new BaseRsp();
        FacadeTracker.FacadeTrackerCore tracker = FacadeTracker.getTracker();

        try {
            Long orderId = Long.parseLong(commentReq.getOrderId());
            OrderWork dbOrderWork = orderWorkService.findOrderWorkByKey(orderId, orderId);

            if (dbOrderWork == null) {
                log.info("【到位】订单不存在: orderId=[{}]", commentReq.getOrderId());

                rsp.setStatus("error");
                rsp.setMsg("订单不存在");
                return rsp;
            }
            tracker.orderId(dbOrderWork.getOrderId());

            Integer channelId = dbOrderWork.getChannelId();
            commentReq.setChannelId(channelId);

            // 保存评价
            CommentDTO commentDTO = new CommentDTO();
            commentDTO.setOrderId(orderId);
            commentDTO.setContent(commentReq.getComment());
            commentDTO.setScore(commentReq.getScore() == null ? 5 : Integer.parseInt(commentReq.getScore()));
            commentDTO.setOperatorType(GlobalConsts.OPERATE_USER_TYPE_CHANNEL);
            commentDTO.setOperatorId(channelId.longValue());
            commentDTO.setOperator(dbOrderWork.getChannelName());

            // 评价
            proxyOrderBService.addComment(commentDTO);
            tracker.remark("保存订单评价");

            // 添加日志
            addCommentLog(commentDTO);

        } catch (Exception e) {
            log.error("【{}】评价失败: {}",
                    DaoweiConsts.getChannelName(commentReq.getChannelId()), e.getMessage(), e);

            rsp.setStatus("error");
            rsp.setMsg("订单异常");

            tracker.error(e, "评价失败");
        }

        return rsp;
    }

    /**
     * 查询服务时间
     * @param req
     * @return
     */
    @Override
    public ServiceTimeRsp queryServiceTime(ServiceTimeReq req) {
        ServiceTimeRsp rsp = new ServiceTimeRsp();

        Double lng = Double.parseDouble(req.getLot());
        Double lat = Double.parseDouble(req.getLat());
        ResponseDTO<com.zmn.base.common.data.common.dro.area.AreaDRO> areaResponse = areaListRemoteService.getAreaByLatAndLng(lng, lat);
        com.zmn.base.common.data.common.dro.area.AreaDRO areaDRO = areaResponse.getData();
        if (areaDRO == null) {
            rsp.setMsg("超出服务范围");
            rsp.setStatus("error");
            return rsp;
        }

        List<LinkedHashMap<String, String>> timeList = Lists.newArrayList();
        Map<String, String> data = getDateMap();

        for (String key: data.keySet()) {
            LinkedHashMap<String, String> dayData = Maps.newLinkedHashMap();
            dayData.put("date", key);
            dayData.put("timeslot", data.get(key));
            timeList.add(dayData);
        }

        Map<String, Object> body = Maps.newHashMap();
        body.put("timeList", timeList);
        rsp.setBody(body);

        return rsp;
    }

    /**
     * 到位直选抢单
     * @param orderId
     */
    @Override
    public void rushToAcceptOrder(Long orderId) throws OmsBaseException {
        OrderWork dbOrderWork = orderWorkService.findOrderWorkByKey(orderId, orderId);
        if (dbOrderWork == null) {
            throw new OmsBaseException(String.format("订单[%s]不存在", orderId));
        }

        FacadeTracker.FacadeTrackerCore tracker = FacadeTracker.getTracker();

        OrderExtend orderExtend = orderExtendService.findMiniOrderExtendByOrderId(orderId);
        String appkey = orderExtend.getExtOuterData();

        RushOrderRsp rushOrderRsp = DaoweiApiInvoker.rushToAcceptOrder(appkey, dbOrderWork);
        StringBuilder orderLog = new StringBuilder();

        // 抢单成功
        RushOrderRsp.OrderInfo rspOrderInfo = rushOrderRsp.getData();
        if (rspOrderInfo != null
                && StringUtils.isNotBlank(rspOrderInfo.getPhone())
                && StringUtils.isNotBlank(rspOrderInfo.getAddress())) {
            UpdateOrderDTO updateOrderDTO = new UpdateOrderDTO();
            updateOrderDTO.setOrderId(dbOrderWork.getOrderId());
            updateOrderDTO.setDisableDiscard(GlobalConsts.NO);
            updateOrderDTO.setPhone(rspOrderInfo.getPhone());
            updateOrderDTO.setAddress(rspOrderInfo.getAddress());

            String logStr = "抢单成功；";
            orderLog.append(logStr);
            proxyOrderBService.updateOrder(updateOrderDTO);
            tracker.remark(logStr);

            // 支付成功，应设置订单自动流转
            try {
                workFlowContextBService.asyncAutoFlow(dbOrderWork.getOrderId(), dbOrderWork.getWorkId());
                tracker.remark("设置订单自动流转");
            } catch (Exception e) {
                tracker.error(e, "设置订单自动流转失败");
            }
        }
        else {
            String logStr = String.format("抢单失败：%s，错误码：%s；", rushOrderRsp.getMsg(), rushOrderRsp.getCode());
            orderLog.append(logStr);
            tracker.remark(logStr);
        }

        //抢单失败，自动取消
        if ("502".equals(rushOrderRsp.getCode())) {
            CancelOrderDTO cancelOrderDTO = new CancelOrderDTO();
            cancelOrderDTO.setOrderId(orderId);
            cancelOrderDTO.setOuterId(dbOrderWork.getOuterId());
            cancelOrderDTO.setChannelId(dbOrderWork.getChannelId());
            cancelOrderDTO.setRemark("抢单失败，自动取消");
            cancelOrderDTO.setOperatorType(GlobalConsts.OPERATE_USER_TYPE_SYSTEM);

            log.info("【到位直选】抢单失败，取消订单: RushOrderRsp:[{}]", JSON.toJSONString(rushOrderRsp));
            String logStr = "抢单失败，自动取消订单；";
            orderLog.append(logStr);

            proxyOrderBService.cancelOrder(cancelOrderDTO);
            tracker.remark(logStr);
            return;
        }

        log.info("【到位直选】抢单失败，不做处理: RushOrderRsp:[{}]", JSON.toJSONString(rushOrderRsp));

        try {
            OrderLogDTO orderLogDTO = new OrderLogDTO();
            orderLogDTO.setOrderId(orderId);
            orderLogDTO.setWorkId(orderId);
            orderLogDTO.setOperatorLogRemark(orderLog.toString());

            saveOrderLog(orderId, orderLogDTO);
        } catch (Exception e) {
            log.error("【{}】保存抢单操作日志失败: orderId=[{}], {}",
                    DaoweiConsts.getChannelName(dbOrderWork.getChannelId()), orderId, e.getMessage());
            tracker.error(e, "保存抢单操作日志失败");
        }
    }

    /**
     * 获取渠道支付二维码地址
     * @param orderId
     * @throws OmsBaseException
     */
    @Override
    public String requestPayQrCodeImgUrl(Long orderId) throws OmsBaseException {
        OrderWork orderWork = orderWorkService.findOrderWorkByKey(orderId, orderId);

        Optional.ofNullable(orderWork.getChannelId())
                .filter(value -> {
                    int valueInt = value.intValue();
                    return DaoweiConsts.CHANNEL_ID_DTW == valueInt
                            || DaoweiConsts.CHANNEL_ID_APP == valueInt
                            || DaoweiConsts.CHANNEL_ID_KYX == valueInt
                            || DaoweiConsts.CHANNEL_ID_ZX == valueInt;
                })
                .orElseThrow(() -> new OmsBaseException("该订单所属渠道不支持二维码支付"));

        if (Objects.equals(GlobalConsts.SERV_CATEG_CLEAN, orderWork.getServCategId())) {
            throw new OmsBaseException("清洗订单不支持二维码支付");
        }

        // 师傅收款额，即为需要补差价金额
        Integer money = Optional.ofNullable(orderWork.getMasterAmount()).orElse(0);
        if (money <= 0) {
            // 没有师傅收款额，不需要同步
            throw new OmsBaseException("该订单不需要收款");
        }

        OrderExtend orderExtend = orderExtendService.findMiniOrderExtendByOrderId(orderId);
        PayQrCodeRsp result = DaoweiApiInvoker.requestPayQrCode(orderExtend.getExtOuterData(), orderWork,
                MoneyUtil.parseToYuan(money));

        String payQrCodeUrl = Optional.ofNullable(result)
                .filter(e -> "ok".equals(e.getStatus()))
                .map(e -> e.getData())
                .orElseThrow(() -> new OmsBaseException("获取二维码失败"));
        return payQrCodeUrl;
    }

    /*===============================================*/
    /*================ Private Method ===============*/
    /*===============================================*/

    private static Map<String, String> getDateMap() {
        Map<String, String> data = Maps.newHashMap();

        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

        // 当前时间
        Calendar calendar = Calendar.getInstance();
        long currentDate = calendar.getTimeInMillis();

        // 走零点算起
        Calendar cal = Calendar.getInstance();
        cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
                cal.get(Calendar.DAY_OF_MONTH), 0, 0, 0);

        String timeslot = "";
        for (int i = 0; i < 48; i++) {
            long beginOfDate = cal.getTimeInMillis();
            // 三分钟内不可预约
            boolean difference = (currentDate - beginOfDate) < 3 * 60 * 1000 && currentDate > beginOfDate;
            if (difference || currentDate > beginOfDate) {
                timeslot += "0";
            } else {
                timeslot += "1";
            }

            cal.add(Calendar.MINUTE, 30);
            cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
                    cal.get(Calendar.DAY_OF_MONTH));
        }

        data.put(format.format(calendar.getTime()) + "", timeslot);
        // 后六天设置成都可预约
        String slot = "111111111111111111111111111111111111111111111111";
        for (int i = 1; i < 7; i++) {
            calendar.add(Calendar.DATE, 1);
            calendar.isSet(Calendar.DATE);
            data.put(format.format(calendar.getTime()) + "", slot);
        }

        return data.entrySet().stream()
                .sorted(Map.Entry.comparingByKey())
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                        (oldValue, newValue) -> oldValue, LinkedHashMap::new));
    }

    /**
     * 将新单数据构建成录单对象
     * @param req
     * @return
     */
    private CreateOrderDTO buildCreateOrderDTO(CreateOrderReq req, DaoweiConsts.ChannelInfo channelInfo) {
        CreateOrderDTO createOrderDTO = new CreateOrderDTO();

        createOrderDTO.setChannelId(channelInfo.getId());
        createOrderDTO.setTelephone(req.getPhone());
        createOrderDTO.setContactName(Optional.ofNullable(req.getContactPerson()).orElse("到位用户"));
        createOrderDTO.setOuterId(req.getOrderId());
        createOrderDTO.setRemark(req.getNote());
        createOrderDTO.setOuterData(JSON.toJSONString(req));

        // 单独保存一下appkey，方便辨识到位APP的清洗和维修单，同步状态会用到
        createOrderDTO.setExtOuterData(req.getAppkey());
        // 到位订单，默认都是登录用户，创建正式用户 lhl-2020.12.17
        createOrderDTO.setLoginPhone(req.getPhone());

        // 预约时间，注意就近转化为：整点和半点
        String expectDate = req.getAppointTime();
        if (org.apache.commons.lang3.StringUtils.isNotBlank(expectDate)) {
            Date dutyTime = DateUtil.parse(expectDate, DateUtil.FORMAT_DEFAULT);
            // 忽略掉当前时间超过预约时间的情况，由线下去确定预约时间
            if (dutyTime.getTime() > System.currentTimeMillis()) {
                createOrderDTO.setDutyTime(DateUtil.round(dutyTime, DateUtil.UNIT_MINUTE));
            }
        }

        try {
            // 地址转换
            _fillAddress(createOrderDTO, req, channelInfo);
            // 商品转换
            _fillProductInfo(createOrderDTO, req, channelInfo);

        } catch (Exception e) {
            log.error("【{}】ErrorMsg:[{}]", channelInfo.getName(), e.getMessage(), e);
            Todm.tracker().error("解析地址/产品错误", e);
            return null;
        }

        // 直选订单中的phone ,address,house 为不完整信息，完整信息会在抢单成功后会返回
        if (DaoweiConsts.CHANNEL_ID_ZX == channelInfo.getId()) {
            createOrderDTO.setTelephone("12345678912"); // 设置一个无效的手机号
        }

        createOrderDTO.setPlat(GlobalConsts.PLAT_MARK_ZMN);
        createOrderDTO.setGender(GlobalDict.GENDER_NO);
        createOrderDTO.setDisableDiscard(GlobalConsts.NO);
        createOrderDTO.setOperator("系统");
        createOrderDTO.setOperatorId((long) GlobalConsts.OPERATE_USER_TYPE_SYSTEM);
        createOrderDTO.setOperatorType(GlobalConsts.OPERATE_USER_TYPE_CHANNEL);
        createOrderDTO.setInputType(OrderConsts.ORDER_INPUT_TYPE_API);

        // 到位活动
        long beginTime = 1606752000000L;    // 2020-12-01 00:00:00
        long endTime = 1672502399000L;      // 2022-12-31 23:59:59
        Long currentTime = System.currentTimeMillis();  // 当前时间
        if (currentTime >= beginTime && currentTime <= endTime) {
            Double discountAmount = 0.0;
            String discountRemark = null;
            if (StringUtils.isNotBlank(req.getDaowayCouponBill())) {

                // 到位优惠不算入渠道优惠中
                // discountAmount = Double.valueOf(req.getDaowayCouponBill());
                discountRemark = String.format("到位优惠活动：平台优惠%.2f元\n", discountAmount);
            }

            if (StringUtils.isNotBlank(req.getShopCouponBill())) {
                discountAmount += Double.valueOf(req.getShopCouponBill());

                if (discountRemark.isEmpty()) {
                    discountRemark = String.format("到位优惠活动：商家优惠%.2f元\n", Double.valueOf(req.getShopCouponBill()));
                } else {
                    discountRemark = String.format("%s,商家优惠%.2f元\n", discountRemark,Double.valueOf(req.getShopCouponBill()));
                }
            }

            // 如果有优惠，使用优惠
            if (discountAmount > 0.0) {
                List<OrderDiscountDTO> orderDiscountList = Lists.newArrayList();
                OrderDiscountDTO orderDiscountDTO = new OrderDiscountDTO();
                orderDiscountDTO.setAmount(MoneyUtil.parseToFen(discountAmount));
                orderDiscountDTO.setType(OrderConsts.ORDER_DISCOUNT_TYPE_LIMIT_FREE);
                orderDiscountDTO.setRemark(discountRemark);
                orderDiscountList.add(orderDiscountDTO);
                createOrderDTO.setOrderDiscountList(orderDiscountList);
                log.info(discountRemark);
            }
        }

        if (DaoweiConsts.CHANNEL_ID_ZX == channelInfo.getId()) {
            // 直选订单，为用户已支付状态
            int channelPrepayAmount = 0;

            StringBuilder logRemarkBuilder = new StringBuilder();
            if (createOrderDTO.getOperatorLogRemark() != null) {
                logRemarkBuilder.append(createOrderDTO.getOperatorLogRemark());
            }

            Integer bill = MoneyUtil.parseToFen(req.getBill());
            if (bill != null) {
                channelPrepayAmount += bill;
                logRemarkBuilder.append("，预付现金").append(MoneyUtil.parseToYuanStr(bill)).append("元");
            }
            if (StringUtil.isNotBlank(req.getDaowayCouponBill())) {
                Integer daowayCouponBill = null;
                try {
                    daowayCouponBill = MoneyUtil.parseToFen(Double.valueOf(req.getDaowayCouponBill()));
                } catch (Exception e) {
                }
                if (daowayCouponBill != null && daowayCouponBill > 0) {
                    channelPrepayAmount += daowayCouponBill;
                    logRemarkBuilder.append("，到位优惠券").append(MoneyUtil.parseToYuanStr(daowayCouponBill)).append("元");
                }
            }

            if (channelPrepayAmount > 0) {
                createOrderDTO.setChannelPrepayAmount(channelPrepayAmount);
                createOrderDTO.setChannelPrepayStatus(PayConsts.PAY_STATUS_DONE);
                createOrderDTO.setOperatorLogRemark(logRemarkBuilder.toString());
            }
            // 直选订单，添加限制：未抢单成功的订单，不允许确认&派单
            createOrderDTO.setDisableDiscard(GlobalConsts.YES);
        }
        else {
            // 非直选订单，添加限制：未付款的订单，不允许确认&派单
            createOrderDTO.setDisableDiscard(GlobalConsts.YES);
        }

        return createOrderDTO;
    }

    /**
     * 补全产品信息
     * @param zmnOrder
     * @param req
     * @param channelInfo
     */
    private void _fillProductInfo(CreateOrderDTO zmnOrder, CreateOrderReq req, DaoweiConsts.ChannelInfo channelInfo)
            throws OmsBaseException {
        StringBuilder productInfo = new StringBuilder("；产品信息：");
        OrderProductDTO orderProduct = null;

        try {
            List<OrderItem> orderItems = JSON.parseArray(req.getItems(), OrderItem.class);

            for (int i=0, len=orderItems.size(); i<len; i++) {
                OrderItem item = orderItems.get(i);

                // 只提取第一个有thirdID的产品作为当前订单的产品
                if (orderProduct == null) {
                    String thirdId = item.getThirdId();
                    if (StringUtils.isNotBlank(thirdId)) {
                        orderProduct = new OrderProductDTO();
                        orderProduct.setShowProductId(Integer.parseInt(thirdId));
                        orderProduct.setProductShowType(GlobalConsts.YES);
                        orderProduct.setNumber(item.getQuantity());
                    }
                }

                if (i > 0) {
                    productInfo.append("，");
                }
                productInfo.append(item.getName()).append("x").append(item.getQuantity());
            }

            if (orderProduct != null) {
                // 校验产品ID合法性

                ProductBaseQuery productDIO = new ProductBaseQuery();
                productDIO.setProductId(orderProduct.getShowProductId());
                log.info("#ProductForeignListRemoteService#listBaseDROByQuery# request: [{}]", productDIO);
                com.zmn.common.dto2.ResponseDTO<List<ProductBaseDRO>> productDROResp
                        = productForeignListRemoteService.listBaseDROByQuery(productDIO);
                log.info("#ProductForeignListRemoteService#listBaseDROByQuery# response: [{}]", productDROResp);
                if (CollectionUtil.isNotNullOrEmpty(productDROResp.getData())) {
                    if (Objects.equals(GlobalConsts.NO, productDROResp.getData().get(0).getShowType())) {
                        orderProduct.setProductId(orderProduct.getShowProductId());
                        orderProduct.setShowProductId(0);
                        orderProduct.setProductShowType(GlobalConsts.NO);
                    }
                }
                else {
                    // 产品ID不合法，产品无效
                    orderProduct = null;
                }
            }
        } catch (Exception e) {
            log.error("【{}】解析产品失败：{}", channelInfo.getName(), e.getMessage(), e);
        }

        if (orderProduct == null) {
            // 默认产品
            orderProduct = new OrderProductDTO();
            orderProduct.setProductId(OrderConsts.CHANNEL_DISABLED_PRODUCT_ID);
            orderProduct.setNumber(1);
        }

        List<OrderProductDTO> orderProducts = new LinkedList<>();
        orderProducts.add(orderProduct);
        zmnOrder.setOrderProductList(orderProducts);

        String productStr = req.getItems();
        if (productInfo.length() >= 0) {
            productStr = productInfo.toString();
        }

        zmnOrder.setOperatorLogRemark(
                Optional.ofNullable(zmnOrder.getOperatorLogRemark()).orElse("") + productStr);
    }

    /**
     * 补全订单地址
     * @param zmnOrder
     * @param req
     * @param channelInfo
     */
    private void _fillAddress(CreateOrderDTO zmnOrder,  CreateOrderReq req, DaoweiConsts.ChannelInfo channelInfo) {
        // 具体地址放入address
        String street = Optional.ofNullable(req.getStreet()).orElse("");
        String address = Optional.ofNullable(req.getHouse()).orElse("");
        zmnOrder.setStreet(street); // street字段在数据库中不能为空
        zmnOrder.setAddress(address);

        // 获取cityId：如果有经纬度，以经纬度获取城市为准；
        if (req.getAddrLat() != null && req.getAddrLng() != null) {
            try {
                Double lng = Double.valueOf(req.getAddrLng());
                Double lat = Double.valueOf(req.getAddrLat());
                log.info(
                        "#AreaListRemoteService#getAreaByLatAndLng# request: lng=[{}], lat=[{}]",
                        req.getAddrLng(),
                        req.getAddrLat()
                );
                com.zmn.common.dto2.ResponseDTO<AreaDRO> areaResponse = areaListRemoteService.getAreaByLatAndLng(lng, lat);
                log.info("#AreaListRemoteService#getAreaByLatAndLng# response: [{}]", areaResponse );
                AreaDRO areaDRO = areaResponse.getData();

                if (areaDRO != null) {
                    zmnOrder.setCityId(areaDRO.getParentId());
                    zmnOrder.setLongitude(lng);
                    zmnOrder.setLatitude(lat);
                }
            } catch (Exception e) {
                log.error("【{}】使用经纬度获取CityId失败:[{}]。Longitude=[{}], Latitude=[{}]",
                        channelInfo.getName(), e.getMessage(), req.getAddrLng(), req.getAddrLat(), e);
            }
        }

        // 记录一下完整地址
        zmnOrder.setOperatorLogRemark(String.format("%s；地址：%s %s",
                Optional.ofNullable(zmnOrder.getOperatorLogRemark()).orElse(""),
                street, address));
    }

    /**
     * 添加评价日志
     */
    private void addCommentLog(CommentDTO commentDTO) {
        try {
            // 处理评价内容
            String msg = String.format("评分：%d分，评论内容：%s", commentDTO.getScore(), commentDTO.getContent());
            if(msg.length() > 2000){
                msg = msg.substring(0, 2000);
            }

            OrderLogDTO orderLogDTO = new OrderLogDTO();
            orderLogDTO.setOrderId(commentDTO.getOrderId());
            orderLogDTO.setOperatorRemark(msg);

            orderLogDTO.setOperatorType(GlobalConsts.OPERATE_USER_TYPE_CHANNEL);
            orderLogDTO.setOperatorId((long) GlobalConsts.OPERATE_USER_TYPE_SYSTEM);
            orderLogDTO.setOperator("系统");

            Todm.tracker().addRemark(msg).addRemark("添加评价日志；");
            proxyOrderBService.addOrderLog(orderLogDTO);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            Todm.tracker().error("添加评价日志失败", e);
        }
    }

    /**
     * 添加超时未支付检查任务
     * @param orderId
     */
    private void addPrepayTimeoutCancelJob(Long orderId) {
        Job job = new Job();
        job.setTaskId(TaskConsts.TASK_ID_CHANNEL_PAY_TIMEOUT_CANCEL + ":" + orderId);
        job.setParam(TaskManager.SHARD_FIELD, TaskConsts.TASK_ID_CHANNEL_PAY_TIMEOUT_CANCEL);
        job.setParam("orderId", orderId.toString());
        Date date = DateUtil.addMinute(DateUtil.getNow(), OrderConsts.ORDER_PREPAY_TIMEOUT_CANCEL_MINUTES);

        Todm.tracker().addRemark("设置超时未支付自动取消");
        taskManager.submitTask(job, -1, DateUtil.dateFormatToString(date, DateUtil.FORMAT_DEFAULT));
    }

    /**
     * 添加抢单任务
     * @param orderId
     */
    private void addRushToAcceptJob(Long orderId, String outerId) {
        Job job = new Job();
        job.setTaskId(TaskConsts.TASK_ID_CHANNEL_RUSH_TO_ACCEPT_ORDER + ":" + orderId);
        job.setParam(TaskManager.SHARD_FIELD, TaskConsts.TASK_ID_CHANNEL_RUSH_TO_ACCEPT_ORDER);
        job.setParam("orderId", orderId.toString());
        job.setParam("outerId", outerId);
        Date date = DateUtil.addSecond(DateUtil.getNow(), 30);

        Todm.tracker().addRemark("设置30秒后自动抢单");
        taskManager.submitTask(job, -1, DateUtil.dateFormatToString(date, DateUtil.FORMAT_DEFAULT));
    }

    /**
     * 添加日志
     *
     * @param orderId
     * @param orderLogDTO
     */
    private void saveOrderLog(Long orderId, OrderLogDTO orderLogDTO) {
        if (orderLogDTO.getType() == null) {
            orderLogDTO.setType(OrderLogConsts.ORDER_LOG_TYPE_CHANNEL_REMARK);
        }

        OrderWork orderWork = orderWorkService.findOrderWorkByKey(orderId, orderId);

        OrderLog orderLog = new OrderLog();
        orderLog.setOrderId(orderId);
        orderLog.setWorkId(orderId);
        orderLog.setType(orderLogDTO.getType());
        orderLog.setTypeName(OrderLogConsts.getOrderLogTypeName(orderLogDTO.getType()));
        orderLog.setBeforeStatus(orderWork.getStatus());
        orderLog.setBeforeStatusName(OrderStatusConsts.getWorkStatusName(orderWork.getStatus()));
        orderLog.setBeforeResultStatus(orderWork.getResultStatus());
        orderLog.setBeforeResultStatusName(OrderStatusConsts.getWorkResultStatusName(orderWork.getResultStatus()));
        orderLog.setAfterStatus(orderWork.getStatus());
        orderLog.setAfterStatusName(OrderStatusConsts.getWorkStatusName(orderWork.getStatus()));
        orderLog.setAfterResultStatus(orderWork.getResultStatus());
        orderLog.setAfterResultStatusName(OrderStatusConsts.getWorkResultStatusName(orderWork.getResultStatus()));
        OrderWorkLogUtils.processOrderLogOperater(orderLog, orderLogDTO);

        StringBuilder sb = new StringBuilder();

        if (StringUtil.isNotBlank(orderLogDTO.getOperatorRemark())) {
            sb.append(orderLogDTO.getOperatorRemark());
        }

        if (StringUtil.isNotBlank(orderLogDTO.getOperatorLogRemark())) {
            sb.append(orderLogDTO.getOperatorLogRemark());
        }

        orderLog.setContent(sb.toString());

        Todm.tracker().addRemark(orderLog.getContent()).addRemark("将申请退款记入操作日志；");
        orderLogBService.save(orderLog);
    }

}
